package com.vcc.boss.controller;


import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaCheckSafe;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.ListUtil;
import cn.idev.excel.ExcelWriter;
import cn.idev.excel.write.metadata.WriteSheet;
import com.mybatisflex.core.paginate.Page;
import com.vcc.boss.core.component.RedisComponent;
import com.vcc.channel.base.model.request.CardCancelDto;
import com.vcc.channel.base.model.request.CardRechargeDto;
import com.vcc.channel.base.model.request.CardWithdrawDto;
import com.vcc.channel.base.model.response.CardInfoVo;
import com.vcc.channel.base.service.ChannelBaseService;
import com.vcc.core.annotations.CurrentUserId;
import com.vcc.core.annotations.RestPack;
import com.vcc.core.constant.CardStatusEnum;
import com.vcc.core.derive.admin.DeriveUserCard;
import com.vcc.core.exception.ProgramException;
import com.vcc.core.util.*;
import com.vcc.data.entity.*;
import com.vcc.service.boss.request.userCard.*;
import com.vcc.service.boss.response.cardholder.CardholderVo;
import com.vcc.service.boss.response.userCard.UserCardInfoVo;
import com.vcc.service.boss.response.userCard.UserCardVoBoss;
import com.vcc.service.sa.BossUserUtil;
import com.vcc.service.service.*;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Log4j2
@RestController
@RequestMapping("bossVccCard")
public class UserCardController {

    @Autowired
    private UserCardService userCardService;
    @Autowired
    private UserCardWalletService userCardWalletService;
    @Autowired
    private UserService userService;
    @Autowired
    private CardTypeService cardService;
    @Autowired
    private WalletService walletService;
    @Autowired
    private CardholderService cardholderService;
    @Autowired
    private RedisComponent redisComponent;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private CardOrderService cardOrderService;

    @SaCheckPermission(value = "card:user-card:list:view")
    @RestPack
    @SaCheckLogin
    @GetMapping("get")
    public UserCardVoBoss get(Long id) {
        UserCard userCard = userCardService.getById(id);
        verifyUser(userCard.getUserId());
        return new UserCardVoBoss(userCard);
    }

    private void verifyUser(Long userId) {
        Long dealerId = userService.getDealerIdById(userId);
        if (!BossUserUtil.hasAllData() && !BossUserUtil.getId().equals(dealerId)) {
            throw new ProgramException("您没有权限操作该用户的卡信息");
        }
    }

    @RestPack
    @SaCheckLogin
    @GetMapping("paging")
    @SaCheckPermission(value = "card:user-card:list:view")
    public Page<UserCardVoBoss> paging(
            Integer pageNumber,
            Integer pageSize,
            Long targetUserId,
            Long id,
            String cardNumberLastFour,
            String startDate,
            String endDate,
            String cardStatus,
            String remark,
            String sorter,
            Long cardTypeId,
            String channelCode
    ) {
        Page<UserCard> page = userCardService.paging(
                Page.of(
                        pageNumber == null || pageNumber < 1 ? 1 : pageNumber,
                        pageSize == null || pageSize < 20 ? 20 : pageSize
                )
                , targetUserId, cardNumberLastFour, remark, startDate, endDate, cardStatus, id, sorter, cardTypeId, channelCode);
        if (page.getRecords().isEmpty()) {
            return page.map(it -> new UserCardVoBoss());
        }
        return page.map(UserCardVoBoss::new);
    }

    @SaCheckPermission(value = "card:user-card:list:derive")
    @RestPack
    @SaCheckLogin
    @GetMapping("derive")
    public void derive(
            Long targetUserId,
            String cardNumberLastFour,
            String startDate,
            String endDate,
            Long id,
            String sortMap,
            String cardStatus,
            String remark,
            Long cardTypeId,
            String channelCode,
            HttpServletResponse response
    ) {
        Long pages = 1L;
        String fileName = System.currentTimeMillis() + "卡片信息.xlsx";
        ExcelWriter excelWriter;
        try {
            ExportUtil.setExcelResponseHeaders(response, fileName);
            excelWriter = ExportUtil.createExcelWriter(response.getOutputStream(), DeriveUserCard.class);
        } catch (Exception e) {
            log.error("导出卡片信息数据失败", e);
            throw new ProgramException("导出卡片信息数据失败");
        }
        WriteSheet writeSheet = ExportUtil.createWriteSheet("卡片信息", DeriveUserCard.class);
        List<String> filterColumns = ListUtil.toList();
        boolean hasHideCardNUmber;
        if (!StpUtil.hasPermission("card:user-card:list:secret:view")) {
            filterColumns.add("cvc");
            filterColumns.add("expiry");
            hasHideCardNUmber = true;
        } else {
            hasHideCardNUmber = false;
        }
        if (!StpUtil.hasPermission("channel:view")) {
            filterColumns.add("channel");
        }
        writeSheet.setExcludeColumnFieldNames(filterColumns);
        Page<UserCard> page;
        do {
            page = userCardService.paging(Page.of(pages++, 500), targetUserId, cardNumberLastFour, remark, startDate, endDate, cardStatus, id, sortMap, cardTypeId, channelCode);
            List<DeriveUserCard> list = page.getRecords().stream().map(it -> DeriveUserCard.builder()
                    .id(it.getId())
                    .userId(it.getUserId())
                    .account(it.getAccount())
                    .channel(it.getChannelCode())
                    .cardNumber(hasHideCardNUmber ? StringUtil.hideCardNumber(it.getCardNumber()) : it.getCardNumber())
                    .cvc(it.getVcc())
                    .expiry(it.getExpiry())
                    .usableQuota(it.getUsableQuota())
                    .remark(it.getRemark())
                    .requestDate(it.getRequestDate())
                    .cardStatus(it.getCardStatus())
                    .firstName(it.getFirstName())
                    .lastName(it.getLastName())
                    .dealerAccount(it.getDealerAccount())
                    .build()).collect(Collectors.toList());
            ExportUtil.writeData(excelWriter, writeSheet, list);
            list = null;
        } while (!(pages).equals(page.getTotalPage() + 1));
        ExportUtil.closeExcelWriter(excelWriter);
    }


    @SaCheckPermission(value = "card:user-card:list:remark:edit")
    @RestPack
    @PostMapping("updateRemark")
    public Boolean updateRemark(
            @RequestBody @Validated UpdateRemarkDto updateRemarkDto
    ) {
        UserCard userCard = userCardService.getById(updateRemarkDto.getId());
        verifyUser(userCard.getUserId());
        return userCardService.updateRemarkById(userCard.getId(), updateRemarkDto.getRemark());
    }

    @RestPack
    @GetMapping("getCardBalance")
    @SaCheckLogin
    public BigDecimal getCardBalance(
            Long id) {
        UserCard userCard = userCardService.getById(id);
        verifyUser(userCard.getUserId());
        ChannelBaseService channelBaseService = applicationContext.getBean(userCard.getChannelCode(), ChannelBaseService.class);
        BigDecimal usableQuota = channelBaseService.queryCardBalance(userCard.getChannelCardId());
        userCardService.updateUsableQuotaById(usableQuota, userCard.getId());
        return usableQuota;
    }

    @SaCheckPermission(value = "card:user-card:list:recharge")
    @RestPack
    @PostMapping("recharge")
    public Boolean recharge(
            @RequestBody @Validated UserCardRechargeDto userCardRechargeDto) {
        if (userCardRechargeDto.getAmount().compareTo(BigDecimal.valueOf(0)) <= 0) {
            throw new ProgramException("金额必须大于0");
        }
        UserCard userCard = userCardService.getById(userCardRechargeDto.getId());
        verifyUser(userCard.getUserId());
        Wallet wallet = walletService.getByUserIdAndCurrencyCode(userCard.getUserId(), userCard.getCurrencyCode());
        if (Objects.equals(wallet.getUsableQuota().compareTo(userCardRechargeDto.getAmount()), -1)) {
            throw new ProgramException("您" + userCard.getCurrencyName() + "钱包余额不足");
        }
        //充值手续费
        BigDecimal tempExchangeRateAmount = null;
        if (Objects.equals(wallet.getExchangeRate().compareTo(BigDecimal.valueOf(0)), 1)) {
            tempExchangeRateAmount = wallet.getExchangeRate().multiply(userCardRechargeDto.getAmount());
            if (Objects.equals(wallet.getUsableQuota().compareTo(userCardRechargeDto.getAmount().add(tempExchangeRateAmount)), -1)) {
                throw new ProgramException("您" + userCard.getCurrencyName() + "钱包余额不足");
            }
        }
        //TODO  调用API
        String orderId = IdWorker.getStringOrderId();
        ChannelBaseService channelBaseService = applicationContext.getBean(userCard.getChannelCode(), ChannelBaseService.class);
        channelBaseService.recharge(
                CardRechargeDto.builder()
                        .channelCardId(userCard.getChannelCardId())
                        .rechargeAmount(userCardRechargeDto.getAmount())
                        .requestId(orderId)
                        .cardNumberId(userCard.getId())
                        .cardNumber(userCard.getCardNumber())
                        .build()
        );
        return true;
    }

    @SaCheckPermission(value = "card:user-card:list:withdraw")
    @RestPack
    @SaCheckLogin
    @PostMapping("out")
    public Boolean out(
            @RequestBody @Validated UserCardOutDto userCardOutDto) {
        if (userCardOutDto.getAmount().compareTo(BigDecimal.valueOf(0)) <= 0) {
            throw new ProgramException("金额必须大于0");
        }
        UserCard userCard = userCardService.getById(userCardOutDto.getId());
        verifyUser(userCard.getUserId());
        //TODO 获取实际卡余额
        ChannelBaseService channelBaseService = applicationContext.getBean(userCard.getChannelCode(), ChannelBaseService.class);
        BigDecimal usableQuota = channelBaseService.queryCardBalance(userCard.getChannelCardId());
        if (Objects.equals(usableQuota.compareTo(userCardOutDto.getAmount()), -1)) {
            throw new ProgramException("您当前卡余额不足，无法转出");
        }
        String orderId = IdWorker.getStringOrderId();
        //TODO 调用APo
        channelBaseService.withdraw(
                CardWithdrawDto.builder()
                        .channelCardId(userCard.getChannelCardId())
                        .withdrawAmount(userCardOutDto.getAmount())
                        .requestId(orderId)
                        .cardNumberId(userCard.getId())
                        .cardNumber(userCard.getCardNumber())
                        .build()
        );
        return true;
    }

    @SaCheckPermission(value = "card:user-card:list:freezeAndUnFreeze")
    @RestPack
    @SaCheckLogin
    @PostMapping("freezeAndUnFreeze")
    public Boolean freezeAndUnFreeze(
            @CurrentUserId Long userId,
            @RequestBody @Validated UserCardFreezeAndUnFreezeDto userCardFreezeAndUnFreezeDto) {
        UserCard userCard = userCardService.getById(userCardFreezeAndUnFreezeDto.getId());
        verifyUser(userCard.getUserId());
        //TODO 获取实际卡状态
        ChannelBaseService channelBaseService = applicationContext.getBean(userCard.getChannelCode(), ChannelBaseService.class);
        CardInfoVo cardInfo = channelBaseService.queryCardInfo(userCard.getChannelCardId());
        String cardStatus = cardInfo.getCardStatus();
        //INACTIVE 待激活
        //ACTIVE 正常
        //REVOKED 已冻结
        //INCANCELED 注销中
        //CANCELED 已注销
        String requestId = IdWorker.getStringOrderId();
        switch (cardStatus) {
            case "INACTIVE" -> throw new ProgramException("当前卡状态为未激活，无法冻结或解冻");
            case "ACTIVE" -> {
                if (userCardFreezeAndUnFreezeDto.getStatus()) {
                    //冻结1
                    channelBaseService.blockCard(userCard.getChannelCardId(), requestId);
                    userCardService.updateCardStatusById(CardStatusEnum.CONTROL.getCode(), userCard.getId());
                } else {
                    userCardService.updateCardStatusById(cardStatus, userCard.getId());
                }
                break;
            }
            case "BLOCKED" -> {
                if (!userCardFreezeAndUnFreezeDto.getStatus()) {
                    if (userCard.getCardStatus().equals(CardStatusEnum.CONTROL.getCode())) {
                        cardOrderService.unblockCard(
                                requestId,
                                userCard.getCardTypeId(),
                                userCard.getUserId(),
                                BossUserUtil.getId(),
                                userCard.getCardNumber(),
                                userCard.getId(),
                                userCard.getChannelCardId(),
                                "风控冻结卡解冻",
                                "SYSTEM"
                        );
                    }
                    channelBaseService.unblockCard(userCard.getChannelCardId(), requestId);
                    userCardService.updateCardStatusById(CardStatusEnum.ACTIVE.getCode(), userCard.getId());
                } else {
                    userCardService.updateCardStatusById(cardStatus, userCard.getId());
                }
                break;
            }
            case "CONTROL" -> {
                userCardService.updateCardStatusById(cardStatus, userCard.getId());
                throw new ProgramException("当前卡状态为管控，无法冻结或解冻");
            }
            case "CLOSE", "ABNORMAL" -> {
                userCardService.updateCardStatusById(cardStatus, userCard.getId());
                throw new ProgramException("当前卡状态为关卡或异常，无法冻结或解冻");
            }
        }
        return true;
    }

    @SaCheckPermission(value = "card:user-card:list:close")
    @RestPack
    @SaCheckLogin
    @PostMapping("close")
    public Boolean close(
            @CurrentUserId Long userId,
            @RequestBody @Validated UserCardCloseDto userCardCloseDto) {
        UserCard userCard = userCardService.getById(userCardCloseDto.getId());
        verifyUser(userCard.getUserId());
        //调用API注销
        ChannelBaseService channelBaseService = applicationContext.getBean(userCard.getChannelCode(), ChannelBaseService.class);
        CardInfoVo cardInfo = channelBaseService.queryCardInfo(userCard.getChannelCardId());
        if (!"ACTIVE".equals(cardInfo.getCardStatus())) {
            throw new ProgramException("当前卡状态不允许注销");
        }
        channelBaseService.cancelCard(
                CardCancelDto.builder()
                        .channelCardId(userCard.getChannelCardId())
                        .requestId(IdWorker.getStringOrderId())
                        .build()
        );
        return true;
    }

    @SaCheckPermission(value = "card:user-card:list:info:view")
    @RestPack
    @SaCheckLogin
    @GetMapping("showUserCardInfo")
    @SaCheckSafe("ADMIN_SHOW_USER_CARD_INFO")
    public UserCardInfoVo showUserCardInfo(
            Long id
    ) {
        UserCard userCard = userCardService.getById(id);
        verifyUser(userCard.getUserId());
        CardholderVo cardholderVo = RegexUtil.checkObjectIsNotNull(userCard.getCardholderId()) ?
                new CardholderVo(cardholderService.getById(userCard.getCardholderId())) :
                null;

        return new UserCardInfoVo(userCard, cardholderVo, StpUtil.hasPermission("card:user-card:list:secret:view"));
    }

}
